﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.IO;

using Zentrum.Collections;

namespace Zentrum.Storage
{
	/// <summary>
	/// Provides access to names resources, such as files, webpages etc etc.
	/// The Zentrum framework uses this to load data required at runtime.
	/// </summary>
	public static partial class StorageManager
	{
		private static Dictionary<string,Func<Uri,Accessor>> s_Accessors=new Dictionary<string,Func<Uri,Accessor>>(CaseInsensitiveStringEqualityComparer.Instance);

		private static readonly object s_Lock=new object();

		static StorageManager()
		{
			s_Accessors["file"]=uri=>new FileAccessor(uri);
			s_Accessors["http"]=uri=>new HttpAccessor(uri);
			s_Accessors["res"]=uri=>new ResourceAccessor(uri);
		}

		/// <summary>
		/// Adds an accessor to the manager
		/// </summary>
		/// <param name="name">The name to register the accessor under</param>
		/// <param name="accessorFactory">The factory to register</param>
		/// <exception cref="System.ArgumentNullException">name is null</exception>
		/// <exception cref="System.ArgumentNullException">accessorFactory is null</exception>
		public static void Add(string name, Func<Uri,Accessor> accessorFactory)
		{
			if(name==null) throw new ArgumentNullException("name");
			if(accessorFactory==null) throw new ArgumentNullException("accessorFactory");
			
			lock(s_Lock)
			{
				if(s_Accessors.ContainsKey(name)) throw new InvalidOperationException("accessor already registered: "+name);
				s_Accessors.Add(name,accessorFactory);
			}
		}

		/// <summary>
		/// Returns the data accessor for the scheme specified in a uri
		/// </summary>
		/// <param name="uri">A uri</param>
		/// <returns>The data accessor for the uri</returns>
		/// <exception cref="System.ArgumentNullException">name is null</exception>
		public static Accessor Get(Uri uri)
		{
			if(uri==null) throw new ArgumentNullException("uri");
			
			Accessor accessor=null;
			
			if(TryGetAcccessor(uri,out accessor))
			{
				return accessor;
			}
			else
			{
				throw new IOException("accessor not found: "+uri.Scheme);
			}			
		}

		/// <summary>
		/// Attempts to get the data access for a uri
		/// </summary>
		/// <param name="uri">The uri whose accesor we're after</param>
		/// <param name="accessor">On success the accessor, or null if no accessor is found</param>
		/// <returns>true if the accessor is found, otherwise false</returns>
		/// <exception cref="System.ArgumentNullException">uri is null</exception>
		public static bool TryGetAcccessor(Uri uri, out Accessor accessor)
		{
			if(uri==null) throw new ArgumentNullException("uri");

			Func<Uri,Accessor> factory=null;

			lock(s_Lock)
			{
				
				if(s_Accessors.TryGetValue(uri.Scheme,out factory)==false)
				{
					accessor=null;
					return false;
				}
			}

			accessor=factory(uri);
			return true;
		}
	}
}
